#pragma once
#include "yzrutil.h"
#include "array/Array.hpp"
#include "array/Arrays.h"
#include "lang/Exception.h"
#include "lang/System.h"
#include <mutex>
#include <shared_mutex>
#include <algorithm>

namespace yzrilyzr_util{
	template<typename T>
	EBCLASS(MemoryFIFOBuffer){
	private:
	static constexpr u_index DEFAULT_CAPACITY=1024;
	static constexpr u_index MAX_CAPACITY=0x7fffffff;

	yzrilyzr_array::Array<T> buffer;
	u_index writePointer=0;
	u_index readPointer=0;
	u_index writeSize=0;
	u_index readSize=0;

	void ensureCapacity(u_index minCapacity){
		if(minCapacity > MAX_CAPACITY){
			throw yzrilyzr_lang::OutOfMemoryError("Required capacity too large");
		}
		u_index oldCapacity=buffer.length;
		if(minCapacity > oldCapacity){
			u_index newCapacity=yzrilyzr_lang::Math::max(minCapacity, oldCapacity + (oldCapacity >> 1)); // 增长50%
			buffer=yzrilyzr_array::Arrays::copyOf(buffer, newCapacity);
		}
	}

	public:
	MemoryFIFOBuffer() : MemoryFIFOBuffer(DEFAULT_CAPACITY){}

	explicit MemoryFIFOBuffer(u_index initialCapacity){
		if(initialCapacity <= 0){
			throw yzrilyzr_lang::IllegalArgumentException("Initial capacity must be positive");
		}
		buffer=yzrilyzr_array::Array<T>(initialCapacity);
	}

	explicit MemoryFIFOBuffer(const yzrilyzr_array::Array<T> &src) : MemoryFIFOBuffer(src.length){
		yzrilyzr_lang::System::arraycopy(src, 0, buffer, 0, src.length);
		writePointer+=src.length;
		writeSize+=src.length;
	}

	~MemoryFIFOBuffer(){}
	/**
	 * 获取写入的总元素计数
	 */
	u_index getWriteSize(){
		return writeSize;
	}

	/**
	 * 获取读取的总元素计数
	 */
	u_index getReadSize(){
		return readSize;
	}

	void write(const yzrilyzr_array::Array<T> &buf){
		write(buf, 0, buf.length);
	}

	void write(const yzrilyzr_array::Array<T> &buf, u_index offset, u_index length){
		Util::checkRange(buf.length, offset, offset + length);
		ensureCapacity(writePointer + length);
		yzrilyzr_lang::System::arraycopy(buf, offset, buffer, writePointer, length);
		writePointer+=length;
		writeSize+=length;
	}

	void write(const T * buf, u_index offset, u_index length){
		ensureCapacity(writePointer + length);
		memcpy(buffer._array + writePointer, buf + offset, length * sizeof(T));
		writePointer+=length;
		writeSize+=length;
	}

	u_index read(yzrilyzr_array::Array<T> &buf){
		return read(buf, 0, buf.length);
	}

	u_index read(yzrilyzr_array::Array<T> &buf, u_index offset, u_index length){
		Util::checkRange(buf.length, offset, offset + length);
		if(available() < length) return -1;
		yzrilyzr_lang::System::arraycopy(buffer, readPointer, buf, offset, length);
		readPointer+=length;
		readSize+=length;
		return length;
	}

	u_index read(T * buf, u_index offset, u_index length){
		length=yzrilyzr_lang::Math::min(available(), length);
		memcpy(buf + offset, buffer._array + readPointer, length * sizeof(T));
		readPointer+=length;
		readSize+=length;
		return length;
	}

	u_index available(){
		return writePointer - readPointer;
	}

	void compact(){
		u_index _available=available();
		yzrilyzr_lang::System::arraycopy(buffer, readPointer, buffer, 0, _available);
		readPointer=0;
		writePointer=_available;
	}

	void reset(){
		readPointer=writePointer=0;
		writeSize=readSize=0;
	}

	yzrilyzr_array::Array<T> toArray(){
		return yzrilyzr_array::Arrays::copyOfRange(buffer, readPointer, writePointer);
	}

	void write(const T & element){
		ensureCapacity(writePointer + 1);
		buffer[writePointer]=element;
		writePointer+=1;
		writeSize+=1;
	}

	bool read(T & element){
		if(writePointer - readPointer == 0){
			return false;
		}

		element=buffer[readPointer];
		readPointer+=1;
		readSize+=1;
		return true;
	}

	u_index skip(u_index n){
		u_index available=writePointer - readPointer;
		n=yzrilyzr_lang::Math::min(available, n);
		readPointer+=n;
		readSize+=n;
		return n;
	}

	u_index capacity(){
		return buffer.length;
	}
	};
}